Skip to content

VisDrone 数据集处理与分析详细实操指南

字数
11387 字
阅读时间
50 分钟

目录

  1. 前言与环境准备
  2. WSL2环境与工具准备
  3. 项目环境搭建
  4. 获取VisDrone数据集
  5. 创建样本数据集
  6. 数据可视化
  7. 数据格式转换
  8. 数据探索性分析
  9. 常见问题与解决方案
  10. 扩展应用与后续步骤

1. 前言与环境准备

1.1 实验目标

本实操指南旨在帮助你完成 VisDrone 数据集的准备、可视化、格式转换和探索性分析。我们将使用 dronefreak/VisDrone-dataset-python-toolkit 工具包,适合无人机图像处理和目标检测任务入门学习。

1.2 技能要求

即使你是新手也能完成,只需具备:

  • 基本的命令行操作能力
  • 简单的 Python 知识
  • 对图像处理有基础了解

1.3 环境要求清单

你需要具备以下环境:

  • 操作系统: Windows 10/11 带有 WSL 2
  • 编程环境: Python 3.8 (通过 Conda 安装)
  • 必要软件:
    • Conda (Anaconda/Miniconda)
    • Git
    • 文本编辑器/IDE (如 VS Code)
    • Web 浏览器

2. WSL 2 环境与工具准备

2.1 确认 WSL 2 已安装并支持 GUI

步骤 1: 检查 WSL 2 状态

  1. 打开 Windows PowerShell 或命令提示符(管理员权限)
  2. 运行以下命令检查 WSL 版本:
wsl -l -v
  1. 确保你的 Linux 发行版 (如 Ubuntu) 显示为 VERSION 2

如果尚未安装 WSL 2,请参照以下步骤:

# 在PowerShell(管理员)中运行
wsl --install

步骤 2: 确认 WSLg 功能可用 较新版本的 Windows 10/11 已默认支持 WSLg,这使你可以在 WSL 2 中运行 GUI 应用程序。

2.2 安装 Miniconda 到 WSL 2

步骤 1: 打开 WSL 2 终端 在 Windows 搜索栏中输入"Ubuntu"或你安装的其他 Linux 发行版名称,打开终端。

步骤 2: 下载 Miniconda 安装脚本

bash
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh

步骤 3: 运行安装脚本

bash
bash Miniconda3-latest-Linux-x86_64.sh

按照提示操作:

  • 阅读并接受许可协议(输入"yes")
  • 确认安装路径(默认为 ~/miniconda3,推荐使用默认路径)
  • 当询问是否初始化 Miniconda 3 时,输入"yes"

步骤 4: 使更改生效 关闭并重新打开 WSL 2 终端,或运行:

bash
source ~/.bashrc

步骤 5: 验证安装

bash
conda --version

应显示已安装的 Conda 版本号。

2.3 加速配置:使用国内镜像源

配置 pip 使用国内镜像(强烈推荐)

  1. 创建 pip 配置目录和文件:
bash
mkdir -p ~/.pip
  1. 用文本编辑器创建或编辑 ~/.pip/pip.conf 文件:
bash
nano ~/.pip/pip.conf
  1. 添加以下内容:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = pypi.tuna.tsinghua.edu.cn
  1. 保存文件(在 nano 中按 Ctrl+O,然后按 Enter,再按 Ctrl+X 退出)

配置 Conda 使用国内镜像(可选但推荐)

bash
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

2.4 安装系统级依赖

这些依赖对于 OpenCV 的 GUI 功能、Git 和其他工具是必需的:

bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y git protobuf-compiler libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev libgtk-3-dev wget unzip

3. 项目环境搭建

3.1 克隆 VisDrone 工具包

步骤 1: 选择工作目录

bash
cd ~  # 或者你希望存放项目的其他目录

步骤 2: 克隆仓库

bash
git clone https://github.com/dronefreak/VisDrone-dataset-python-toolkit.git
cd VisDrone-dataset-python-toolkit

📝 提示: 当前工作目录应为 ~/VisDrone-dataset-python-toolkit

3.2 创建并激活 Conda 环境

步骤 1: 创建名为 visdrone_env 的 Python 3.8 环境

bash
conda create -n visdrone_env python=3.8 -y

步骤 2: 激活环境

bash
conda activate visdrone_env

你的终端提示符前应该出现 (visdrone_env),表示环境已激活。

3.3 安装 Python 依赖包

使用配置好的国内 pip 镜像安装核心库:

bash
pip install numpy opencv-python matplotlib tensorflow jupyter notebook pandas seaborn scikit-learn tqdm

⚠️ 注意: 如果遇到依赖冲突,请尝试先卸载冲突包,例如:

bash
Pip uninstall typing-extensions -y

然后重新运行完整的安装命令。

4. 获取 VisDrone 数据集

VisDrone 数据集包含多个任务数据集,我们主要关注目标检测任务。

4.1 官方数据集信息

数据集来源: 天津大学 AISKYEYE 团队收集的无人机航拍图像和视频数据集。

数据集下载链接:

目标检测训练集(VisDrone-DET trainset, 1.44 GB):

目标检测验证集(VisDrone-DET valset, 0.07 GB):

目标检测测试集-开发版(VisDrone-DET testset-dev, 0.28 GB):

4.2 下载与解压数据集

步骤 1: 创建数据存放目录

bash
mkdir -p ~/VisDrone_datasets
cd ~/VisDrone_datasets

步骤 2: 下载数据集文件 从上述链接下载至少一个数据集(推荐训练集)。 你可以使用以下方式之一:

  • 通过浏览器下载后,将文件移动到 WSL 中
  • 直接在 WSL 中使用 wget 下载(如果有直接链接)

步骤 3: 解压数据集文件

bash
unzip VisDrone2019-DET-train.zip  # 解压训练集,文件名可能不同

数据集结构: 解压后,你会得到一个结构如下的目录:

VisDrone2019-DET-train/
├── annotations/  # 包含.txt标注文件
└── images/       # 包含.jpg图像文件

5. 创建样本数据集

为了快速测试工具包功能,我们将从完整数据集创建一个小型样本数据集。

5.1 创建样本目录结构

步骤 1: 在项目根目录创建样本目录

bash
cd ~/VisDrone-dataset-python-toolkit  # 确保在项目根目录
mkdir -p data/samples/images
mkdir -p data/samples/annotations

5.2 复制样本文件

步骤 1: 选择几个样本文件 从完整数据集中选择 5-10 个图像和对应的标注文件:

bash
# 假设完整数据集位于~/VisDrone_datasets/VisDrone2019-DET-train/
# 复制10个图像文件
ls ~/VisDrone_datasets/VisDrone2019-DET-train/images/ | head -10 | xargs -I{} cp ~/VisDrone_datasets/VisDrone2019-DET-train/images/{} data/samples/images/

# 复制对应的标注文件
ls data/samples/images/ | sed 's/\.jpg/\.txt/g' | xargs -I{} cp ~/VisDrone_datasets/VisDrone2019-DET-train/annotations/{} data/samples/annotations/

步骤 2: 验证样本数据集结构

bash
ls -l data/samples/images/
ls -l data/samples/annotations/

确保图像和标注文件数量相同,且文件名(不含扩展名)一一对应。

6. 数据可视化

现在我们将创建一个脚本来可视化 VisDrone 数据,并在图像上标注目标边界框。

6.1 创建可视化脚本

步骤 1: 创建脚本文件 在项目根目录下创建 enhanced_visualize.py 文件:

bash
nano enhanced_visualize.py

步骤 2: 添加以下代码

python
# enhanced_visualize.py 
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import argparse
from datetime import datetime
import time

# VisDrone类别定义 (中文)
CATEGORIES = {
    0: '忽略区域', 1: '行人', 2: '人群', 3: '自行车', 4: '汽车',
    5: '面包车', 6: '卡车', 7: '三轮车', 8: '遮棚三轮车',
    9: '公交车', 10: '摩托车', 11: '其他'
}

def visualize_image_annotations(image_path, annotation_path, output_dir="visual_output", show_image=False):
    """
    可视化单个图像及其标注,并在图像上添加统计信息。
    """
    if not os.path.exists(image_path):
        print(f"错误: 图像文件未找到 {image_path}")
        return
    if not os.path.exists(annotation_path):
        print(f"错误: 标注文件未找到 {annotation_path}")
        return

    # 读取图像
    image = cv2.imread(image_path)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Matplotlib 使用 RGB
    img_h, img_w = image.shape[:2]

    # 读取标注
    with open(annotation_path, 'r') as f:
        annotations = [line.strip().split(',') for line in f if line.strip()]

    fig, ax = plt.subplots(1, figsize=(14, 10))
    ax.imshow(image_rgb)
    ax.axis('off') # 不显示坐标轴

    obj_counts = {}
    total_objs = 0

    # 随机颜色映射,确保每个类别颜色不同
    colors = plt.cm.get_cmap('tab20', len(CATEGORIES))

    for ann in annotations:
        if len(ann) < 6: continue # 跳过格式不正确的标注

        x, y, w, h = int(ann[0]), int(ann[1]), int(ann[2]), int(ann[3])
        category_id = int(ann[5])

        # 统计各类物体
        if category_id > 0: # 忽略类别0 (忽略区域)
            category_name = CATEGORIES.get(category_id, f"未知类别{category_id}")
            obj_counts[category_name] = obj_counts.get(category_name, 0) + 1
            total_objs += 1

            # 绘制边界框
            rect_color = colors(category_id % len(CATEGORIES)) # 使用模运算防止索引越界
            rect = Rectangle((x, y), w, h, linewidth=2, edgecolor=rect_color, facecolor='none')
            ax.add_patch(rect)

            # 添加类别标签
            ax.text(x, y - 10, category_name, color='white', fontsize=8,
                    bbox=dict(facecolor=rect_color, alpha=0.7, pad=0.2))

    # 在图像上添加统计信息
    stats_text_lines = [f"图像: {os.path.basename(image_path)}",
                        f"尺寸: {img_w}x{img_h}px",
                        f"总检测目标数: {total_objs}"]
    stats_text_lines.extend([f"- {cat}: {count}" for cat, count in sorted(obj_counts.items())])

    y_offset = 20
    for i, line_text in enumerate(stats_text_lines):
        ax.text(10, y_offset + i * 18, line_text, color='lime', fontsize=9,
                bbox=dict(facecolor='black', alpha=0.6, pad=1))

    # 保存图像
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    output_filename = os.path.join(output_dir, f"vis_{os.path.basename(image_path)}")
    plt.savefig(output_filename, bbox_inches='tight', dpi=150)
    print(f"可视化结果已保存到: {output_filename}")

    if show_image:
        plt.show()
    plt.close(fig) # 关闭图像,释放内存

def find_dataset_paths(base_data_dir):
    """尝试在给定目录下找到images和annotations子目录"""
    for root, dirs, _ in os.walk(base_data_dir):
        if "images" in dirs and "annotations" in dirs:
            return os.path.join(root, "images"), os.path.join(root, "annotations")
    print(f"错误: 在 {base_data_dir} 下未找到 'images' 和 'annotations' 目录。")
    return None, None

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="VisDrone 数据集可视化工具")
    parser.add_argument("--data_root", type=str, default="data", help="包含VisDrone数据集的根目录 (如 'data/VisDrone2019-DET-train' 或 'data')")
    parser.add_argument("--output_vis_dir", type=str, default="visual_output", help="保存可视化结果的目录")
    parser.add_argument("--max_vis_images", type=int, default=5, help="最多可视化多少张图片")
    parser.add_argument("--show", action="store_true", help="是否在处理时显示每张图片")
    args = parser.parse_args()

    images_dir, annotations_dir = find_dataset_paths(args.data_root)

    if images_dir and annotations_dir:
        print(f"使用图像目录: {images_dir}")
        print(f"使用标注目录: {annotations_dir}")

        image_files = sorted([f for f in os.listdir(images_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))])

        processed_count = 0
        for img_file in image_files:
            if processed_count >= args.max_vis_images:
                break

            base_name = os.path.splitext(img_file)[0]
            image_path = os.path.join(images_dir, img_file)
            annotation_path = os.path.join(annotations_dir, f"{base_name}.txt")

            print(f"\n正在处理: {img_file}...")
            visualize_image_annotations(image_path, annotation_path, args.output_vis_dir, args.show)
            processed_count += 1

        print(f"\n可视化完成! 共处理 {processed_count} 张图片。")
    else:
        print("未能找到数据集路径,请检查 --data_root 参数。")

步骤3: 保存脚本 在nano编辑器中按Ctrl+O然后Enter保存文件,再按Ctrl+X退出。

6.2 运行可视化脚本

运行脚本并处理样本数据

bash
python enhanced_visualize.py --data_root data/samples --max_vis_images 5 --output_vis_dir visualizations_enhanced

命令参数解释:

  • --data_root data/samples: 指向我们手动创建的样本数据目录
  • --max_vis_images 5: 最多处理5张图片(可以根据样本数量调整)
  • --output_vis_dir visualizations_enhanced: 输出目录名称

6.3 检查可视化结果

步骤1: 打开输出目录

bash
ls -l visualizations_enhanced/

步骤2: 查看可视化图像 你可以通过多种方式查看生成的图像:

  1. 使用WSLg的图像查看器:
bash
eog visualizations_enhanced/vis_*.jpg  # 若已安装eog (Eye of GNOME)
# 或
xdg-open visualizations_enhanced/  # 打开文件夹
  1. 通过Windows资源管理器访问: Windows资源管理器可以直接访问WSL文件系统。在Windows地址栏输入:
\\wsl$\Ubuntu\home\你的用户名\VisDrone-dataset-python-toolkit\visualizations_enhanced

这些图像应显示带有彩色边界框、类别标签和统计信息的无人机图像。

7. 数据格式转换

VisDrone 数据集使用其特定的标注格式。不同的目标检测框架可能需要不同的标注格式。

7.1 转换为 PASCAL-VOC 格式 (使用工具包自带脚本)

dronefreak/VisDrone-dataset-python-toolkit 工具包内主要包含一个用于将 VisDrone 标注转换为 PASCAL-VOC XML 格式的脚本:convertVis_to_xml.py。PASCAL-VOC 是一种广泛支持的格式。

步骤 1: 确认脚本位置 该脚本 convertVis_to_xml.py 应该位于你克隆的 VisDrone-dataset-python-toolkit 项目的根目录下。

步骤 2: (示例) 执行转换为 PASCAL-VOC 格式 你需要根据 convertVis_to_xml.py 脚本的具体用法来执行。通常,你要指定输入(VisDrone 格式的标注和图像)和输出目录。请查阅该脚本的说明或其仓库的 README 文件获取确切的命令行参数。

把训练库放到工具包的文件夹后运行命令

bash
# 示例命令,具体请参考 convertVis_to_xml.py 的实际用法
python convertVis_to_xml.py
# (可能还需要图像路径等其他参数)

8. 数据探索性分析

使用Jupyter Notebook进行数据探索分析,可视化数据分布特征。

8.1 启动Jupyter Notebook

步骤1: 确保在项目根目录

bash
cd ~/VisDrone-dataset-python-toolkit

步骤2: 启动Notebook服务器

bash
jupyter notebook --ip=0.0.0.0 --no-browser

步骤3: 访问Notebook 服务器启动后,终端将显示URL和访问令牌。复制URL(通常类似于http://127.0.0.1:8888/?token=...)到浏览器访问Jupyter界面。

8.2 创建并编写分析Notebook

步骤1: 创建新Notebook 在Jupyter界面中:

  1. 点击右上角的"New"按钮
  2. 选择"Python 3"创建新的Notebook
  3. 重命名为"VisDrone_EDA_Samples.ipynb"(点击顶部的"Untitled",输入新名称)

步骤2: 添加代码单元格

单元格1: 导入库和基本设置

python
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import cv2 # OpenCV
from collections import Counter
import glob # 用于查找文件路径
import json

# Matplotlib 和 Seaborn 的美化设置
%matplotlib inline
plt.style.use('ggplot')
sns.set_palette("viridis") # 设置颜色主题
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 (如果系统有此字体)
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

print("库导入完成,环境设置完毕!")

单元格2: 定义类别和数据路径

python
# VisDrone类别定义 (与 enhanced_visualize.py 中一致)
CATEGORIES = {
    0: '忽略区域', 1: '行人', 2: '人群', 3: '自行车', 4: '汽车',
    5: '面包车', 6: '卡车', 7: '三轮车', 8: '遮棚三轮车',
    9: '公交车', 10: '摩托车', 11: '其他'
}

# 自动查找数据集路径的函数
def find_dataset_paths_notebook(base_data_dir, subfolder_name=None):
    """在基础目录下查找images和annotations子目录"""
    search_dir = base_data_dir
    if subfolder_name:
        search_dir = os.path.join(base_data_dir, subfolder_name)
  
    # 首先检查直接子目录结构
    images_dir = os.path.join(search_dir, "images")
    annotations_dir = os.path.join(search_dir, "annotations")
  
    if os.path.isdir(images_dir) and os.path.isdir(annotations_dir):
        print(f"在 {search_dir} 直接找到数据集。")
        return images_dir, annotations_dir
  
    # 然后递归搜索
    for root, dirs, _ in os.walk(search_dir):
        if "images" in dirs and "annotations" in dirs:
            print(f"在 {root} 找到数据集。")
            return os.path.join(root, "images"), os.path.join(root, "annotations")
  
    print(f"警告: 在 {search_dir} 下未找到 'images' 和 'annotations' 目录。")
    return None, None

# 设置数据集根目录
dataset_base_dir = "data"
# 为样本数据指定subfolder_name
images_dir, annotations_dir = find_dataset_paths_notebook(dataset_base_dir, subfolder_name="samples")

print(f"图像目录: {images_dir}")
print(f"标注目录: {annotations_dir}")

# 如果未找到,可以直接指定路径
if not images_dir or not annotations_dir:
    images_dir = "data/samples/images"
    annotations_dir = "data/samples/annotations"
    print(f"手动设置 - 图像目录: {images_dir}")
    print(f"手动设置 - 标注目录: {annotations_dir}")

单元格3: 加载和解析标注数据

python
all_annotations_data = []

if annotations_dir and os.path.exists(annotations_dir):
    annotation_files = sorted(glob.glob(os.path.join(annotations_dir, "*.txt")))
    print(f"找到 {len(annotation_files)} 个标注文件。")

    for ann_file_path in annotation_files:
        image_filename_base = os.path.splitext(os.path.basename(ann_file_path))[0]

        with open(ann_file_path, 'r') as f:
            for line in f:
                parts = line.strip().split(',')
                if len(parts) < 6: continue # 确保标注格式基本正确

                try:
                    bbox_left = int(parts[0])
                    bbox_top = int(parts[1])
                    bbox_width = int(parts[2])
                    bbox_height = int(parts[3])
                    # score = int(parts[4]) # 根据VisDrone格式,score为1表示物体,0表示忽略
                    category_id = int(parts[5])
                    # truncation = int(parts[6])
                    # occlusion = int(parts[7])

                    # 计算面积和长宽比
                    area = bbox_width * bbox_height
                    aspect_ratio = bbox_width / bbox_height if bbox_height > 0 else 0

                    all_annotations_data.append({
                        'image_id': image_filename_base,
                        'category_id': category_id,
                        'category_name': CATEGORIES.get(category_id, f"未知{category_id}"),
                        'x': bbox_left,
                        'y': bbox_top,
                        'width': bbox_width,
                        'height': bbox_height,
                        'area': area,
                        'aspect_ratio': aspect_ratio
                    })
                except ValueError:
                    print(f"警告: 解析标注行失败 {parts} in {ann_file_path}")
                    continue
else:
    print("错误: 标注目录未找到或不存在,无法加载数据。")
    
# 创建DataFrame
df_annotations = pd.DataFrame(all_annotations_data)

if not df_annotations.empty:
    print(f"\n成功加载 {len(df_annotations)} 条标注数据。")
    print("DataFrame 前5行预览:")
    display(df_annotations.head())
    print("\nDataFrame 基本信息:")
    df_annotations.info()
else:
    print("\n未能加载任何标注数据到DataFrame。")

单元格4: 目标类别分布统计与可视化

python
if not df_annotations.empty and 'category_name' in df_annotations.columns:
    # 过滤掉"忽略区域"进行主要类别分析
    df_objects_only = df_annotations[df_annotations['category_id'] != 0]

    plt.figure(figsize=(12, 7))
    category_counts = df_objects_only['category_name'].value_counts()
    sns.barplot(x=category_counts.index, y=category_counts.values)
    plt.title('数据集中各目标类别数量分布 (不含忽略区域)', fontsize=16)
    plt.xlabel('目标类别', fontsize=12)
    plt.ylabel('数量', fontsize=12)
    plt.xticks(rotation=45, ha="right", fontsize=10)
    plt.tight_layout() # 调整布局以防止标签重叠
    plt.show()

    print("\n各类别数量:")
    print(category_counts)
else:
    print("DataFrame为空或缺少'category_name'列,无法进行类别分布分析。")

单元格5: 目标区域面积分布

python
if not df_annotations.empty and 'area' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
      
    plt.figure(figsize=(10, 6))
    # 使用对数刻度可能有助于观察广泛分布的面积
    sns.histplot(df_objects_only['area'], bins=50, kde=True, log_scale=(False, True)) # Y轴对数刻度
    plt.title('目标边界框面积分布', fontsize=16)
    plt.xlabel('面积 (像素平方)', fontsize=12)
    plt.ylabel('频数 (对数刻度)', fontsize=12)
    plt.show()

    print("\n面积描述性统计:")
    print(df_objects_only['area'].describe())
else:
    print("DataFrame为空或缺少'area'列,无法进行面积分布分析。")

单元格6: 每张图像中的目标数量分布

python
if not df_annotations.empty and 'image_id' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
      
    objects_per_image = df_objects_only.groupby('image_id').size()

    plt.figure(figsize=(10, 6))
    sns.histplot(objects_per_image, bins=30, kde=False)
    plt.title('每张图像中的目标数量分布', fontsize=16)
    plt.xlabel('图像中的目标数量', fontsize=12)
    plt.ylabel('图像数量', fontsize=12)
    plt.show()

    print("\n每张图像目标数量描述性统计:")
    print(objects_per_image.describe())
else:
    print("DataFrame为空或缺少'image_id'列,无法进行图像目标数量分析。")

单元格7: 目标边界框长宽比分布

python
if not df_annotations.empty and 'aspect_ratio' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 过滤掉极端或无效的长宽比值,例如 area=0 或 height=0 导致的长宽比为0或inf
    valid_aspect_ratios = df_objects_only[(df_objects_only['aspect_ratio'] > 0) &
                                          (df_objects_only['aspect_ratio'] < 10)]['aspect_ratio']
                                          # 设定一个合理的上限,如10

    plt.figure(figsize=(10, 6))
    sns.histplot(valid_aspect_ratios, bins=50, kde=True)
    plt.title('目标边界框长宽比分布 (0 < AR < 10)', fontsize=16)
    plt.xlabel('长宽比 (宽度/高度)', fontsize=12)
    plt.ylabel('频数', fontsize=12)
    plt.show()

    print("\n长宽比描述性统计:")
    print(valid_aspect_ratios.describe())
else:
    print("DataFrame为空或缺少'aspect_ratio'列,无法进行长宽比分析。")

单元格8: 不同类别的目标尺寸比较 (箱线图)

python
if not df_annotations.empty and 'category_name' in df_annotations.columns and 'area' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 选择前6个最常见的类别,以避免图表过于拥挤
    top_categories = df_objects_only['category_name'].value_counts().nlargest(6).index.tolist()
    df_top_categories = df_objects_only[df_objects_only['category_name'].isin(top_categories)]
  
    plt.figure(figsize=(14, 8))
    sns.boxplot(x='category_name', y='area', data=df_top_categories)
    plt.title('不同类别目标的面积分布 (箱线图)', fontsize=16)
    plt.xlabel('目标类别', fontsize=12)
    plt.ylabel('面积 (像素平方)', fontsize=12)
    plt.yscale('log')  # 对数刻度更容易看清差异
    plt.xticks(rotation=45, ha="right")
    plt.tight_layout()
    plt.show()
else:
    print("DataFrame为空或缺少必要列,无法进行类别尺寸比较分析。")

单元格9: 热力图 - 目标位置分布

python
if not df_annotations.empty and 'x' in df_annotations.columns and 'y' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 创建位置热力图
    plt.figure(figsize=(12, 10))
  
    # 计算目标的中心点
    df_objects_only['center_x'] = df_objects_only['x'] + df_objects_only['width'] / 2
    df_objects_only['center_y'] = df_objects_only['y'] + df_objects_only['height'] / 2
  
    # 使用KDE图显示目标位置分布
    sns.kdeplot(
        x=df_objects_only['center_x'],
        y=df_objects_only['center_y'],
        cmap="hot",
        fill=True,
        thresh=0,
        levels=100
    )
  
    plt.title('目标位置分布热力图', fontsize=16)
    plt.xlabel('图像X坐标', fontsize=12)
    plt.ylabel('图像Y坐标', fontsize=12)
    plt.colorbar(label='密度')
    plt.show()
else:
    print("DataFrame为空或缺少必要的坐标列,无法创建位置热力图。")

步骤3: 运行并分析结果 依次执行每个代码单元格(点击单元格,然后按Shift+Enter或单击工具栏中的运行按钮)。

观察每个可视化结果,了解数据集的以下特性:

  • 各类别对象的分布情况
  • 目标尺寸(面积)的分布特征
  • 每张图像中目标数量的分布
  • 目标形状特征(通过长宽比)
  • 不同类别目标的尺寸差异
  • 目标在图像中的位置分布

9. 常见问题与解决方案

9.1 中文字体显示问题

问题: 可视化图表中的中文显示为方块或乱码。

解决方案:

  1. 安装中文字体包:
bash
sudo apt install -y fonts-wqy-zenhei fonts-wqy-microhei
  1. 更新字体缓存:
bash
fc-cache -fv
  1. 在代码中明确指定可用的中文字体:
python
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei', 'WenQuanYi Micro Hei', 'SimHei', 'AR PL UMing CN']

9.2 依赖冲突问题

问题: 安装Python包时出现依赖冲突错误。

解决方案:

  1. 创建新的干净环境:
bash
conda create -n visdrone_new python=3.8 -y
conda activate visdrone_new
  1. 按特定顺序安装依赖:
bash
pip install numpy
pip install opencv-python matplotlib
pip install tensorflow jupyter notebook pandas seaborn scikit-learn tqdm

9.3 内存不足问题

问题: 处理大型数据集时遇到内存不足(特别是在Jupyter Notebook中)。

解决方案:

  1. 分批处理数据:
python
# 示例:分批读取与处理
batch_size = 1000
for i in range(0, len(all_files), batch_size):
    batch_files = all_files[i:i+batch_size]
    # 处理这个批次
    process_batch(batch_files)
    # 清理内存
    gc.collect()
  1. 减少加载的数据量:
python
# 随机抽样10%的数据进行分析
sampled_files = random.sample(all_files, int(len(all_files) * 0.1))

9.4 找不到文件或目录

问题: 脚本无法找到指定的数据集目录或文件。

解决方案:

  1. 使用绝对路径:
bash
python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone2019-DET-train
  1. 手动检查文件结构:
bash
ls -la ~/VisDrone_datasets/  # 检查顶层目录
ls -la ~/VisDrone_datasets/VisDrone2019-DET-train/  # 检查子目录
find ~/VisDrone_datasets/ -name "*.jpg" | head  # 检查是否有jpg文件

9.5 WSLg显示问题

问题: 在WSL环境下,尝试运行需要图形界面的Python脚本(例如使用OpenCV的 cv2.imshow() 进行图像可视化)时,无法正常显示可视化窗口,或者出现类似 "cannot open display" 的错误。

解决方案:

以下是几种排查和解决WSLg显示问题的方法,建议按顺序尝试:

  • 方法一:检查并尝试设置 DISPLAY 变量 (推荐首先尝试)

    1. 检查当前 DISPLAY: 在你的 WSL 终端 (激活了 visdrone_env 的那个),检查当前的 DISPLAY 环境变量:

      bash
      (visdrone_env) your_user@your_machine:~$ echo $DISPLAY

      根据你遇到的错误,它可能会输出一个具体的IP地址和端口号,例如 172.23.176.1:0

    2. 尝试 WSLg 默认 DISPLAY: 如果你的 Windows 10/11 版本较新,应该支持 WSLg。WSLg 通常期望 DISPLAY 变量设置为 :0 (指向 WSLg 内部的 Wayland compositor)。尝试手动设置它:

      bash
      (visdrone_env) your_user@your_machine:~$ export DISPLAY=:0
    3. 测试脚本: 在同一个终端中,立即再次尝试运行你的 GUI 测试脚本 (例如 test_cv_gui.py 或其他包含 cv2.imshow() 的可视化脚本):

      bash
      (visdrone_env) your_user@your_machine:~$ python your_gui_script.py
    4. 结果判断与持久化设置

      • 如果这样可行 (脚本成功显示了图形界面)
        • 这说明 WSLg 已激活,问题通过设置 DISPLAY=:0 解决了。
        • 为了方便,你可以将 export DISPLAY=:0 添加到你的 WSL shell 配置文件中,使其在每次打开新终端时自动生效。
          • 如果你使用 bash (默认shell):
            bash
            echo 'export DISPLAY=:0' >> ~/.bashrc
            source ~/.bashrc  # 使更改立即生效或重新打开终端
          • 如果你使用 zsh:
            bash
            echo 'export DISPLAY=:0' >> ~/.zshrc
            source ~/.zshrc  # 使更改立即生效或重新打开终端
        • 注意: 虽然 WSLg 旨在自动处理 DISPLAY变量,但在某些情况下,手动设置可能有所帮助。如果将来 WSLg 的行为发生变化,或者你使用了其他 X 服务器(如 VcXsrv 并配置了特定的 IP 地址),这个固定的设置可能会与之冲突,届时你可能需要从配置文件中移除或注释掉这一行。
      • 如果仍然不行:继续尝试下面的方法。
  • 方法二:确认 WSLg 基础功能是否正常

    1. 安装 x11-apps (如果尚未安装): 这个软件包包含一些简单的 X11 图形应用程序,可以用来测试基础的图形显示功能。

      bash
      sudo apt update
      sudo apt install -y x11-apps
    2. 运行 xeyes 测试

      bash
      xeyes
    3. 结果判断:

      • 如果屏幕上显示了一双跟随鼠标指针移动的眼睛图标,那么 WSLg 的基础图形显示功能是正常的。问题可能更多地与你的 Python 环境、OpenCV 的 GUI 后端或其他特定于应用的配置有关。
      • 如果 xeyes 也不显示,或者报错,那么 WSLg 本身可能没有正确启动或配置。你需要:
        • 检查你的 Windows 版本是否支持 WSLg (通常需要 Windows 10 build 21364+ 或 Windows 11)。
        • 确保 WSL 已更新到最新版本 (wsl --update)。
        • 检查 WSLg 的相关组件是否正常运行。
  • 方法三:间接验证——通过文件系统查看生成的图像 如果你的脚本的主要目的是处理数据并生成图像文件(即使可视化窗口无法显示),你可以检查文件是否已按预期生成。

    1. 定位输出目录: 确定你的脚本将可视化结果保存到了哪个目录。例如,教程中提到的路径是 VisDrone-dataset-python-toolkit/visualizations_enhanced

    2. 通过 Windows 文件资源管理器访问: 在 Windows 文件资源管理器的地址栏中,输入以下格式的路径(请将 Ubuntu 替换为你的WSL发行版名称,你的用户名 替换为你的实际 WSL 用户名,并根据实际情况调整后续路径):

      \\wsl$\Ubuntu\home\你的用户名\VisDrone-dataset-python-toolkit\visualizations_enhanced

      或者,如果你的脚本将文件保存在WSL中挂载的Windows驱动器路径下 (例如 /mnt/c/Users/YourWindowsUser/output_images),你可以直接在Windows中导航到该路径 (C:\Users\YourWindowsUser\output_images)。

    3. 检查文件: 查看该目录下是否有新生成的图像文件。这可以帮助判断脚本的核心逻辑(不包括GUI显示部分)是否在正常工作。如果文件存在且内容正确,那么问题主要集中在GUI的显示环节。

进一步排查方向 (如果以上方法均无效):

  • Python OpenCV GUI 后端: 确保你安装的 OpenCV (通常是 opencv-python 包) 是带有 GUI 支持的版本。opencv-python-headless 版本不包含 GUI 功能。
  • 虚拟环境依赖: 再次确认在 visdrone_env 虚拟环境中,所有必要的依赖项(包括 opencv-python)都已正确安装且版本兼容。
  • Windows 防火墙或安全软件: 极少数情况下,防火墙或安全软件可能会干扰 WSLg 的通信。
  • 查看详细错误信息: 当 GUI 脚本失败时,仔细阅读终端中输出的完整错误信息,它们通常会包含定位问题的关键线索。
  • 重启WSL: 尝试完全关闭并重启WSL服务:在PowerShell或CMD中运行 wsl --shutdown,然后重新打开WSL终端。

10. 扩展应用与后续步骤

完成本教程后,你可以进一步探索以下方向:

10.1 处理完整数据集

步骤1: 使用完整数据集运行工具

bash
# 可视化
python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone2019-DET-train --max_vis_images 20 --output_vis_dir full_visualizations

# 格式转换
python visdrone2coco.py --data_dir ~/VisDrone_datasets/VisDrone2019-DET-train --output_dir full_coco_output
python visdrone2yolo.py --data_dir ~/VisDrone_datasets/VisDrone2019-DET-train --output_dir full_yolo_output

步骤2: 对完整数据集进行分析 创建新的Jupyter Notebook,使用与第8节相同的代码,但将dataset_base_dir调整为完整数据集路径。

10.2 训练目标检测模型

使用转换后的数据集训练深度学习目标检测模型。

YOLOv5示例:

bash
# 克隆YOLOv5仓库
git clone https://github.com/ultralytics/yolov5
cd yolov5

# 安装依赖
pip install -r requirements.txt

# 创建数据配置文件 (visdrone.yaml)
# 使用我们转换的YOLO格式数据训练
python train.py --img 640 --batch 16 --epochs 50 --data visdrone.yaml --weights yolov5s.pt

COCO格式与TensorFlow Object Detection API示例: 可以参考TensorFlow官方文档,使用我们转换的COCO格式数据。

10.3 自定义数据处理工具

开发自己的脚本来扩展VisDrone工具包功能。

示例: 数据增强脚本

python
# data_augmentation.py
import cv2
import numpy as np
import os
import glob
from tqdm import tqdm
import imgaug.augmenters as iaa
import imgaug as ia
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage

# 定义增强序列
seq = iaa.Sequential([
    iaa.Fliplr(0.5),  # 水平翻转
    iaa.Affine(rotate=(-10, 10)),  # 旋转
    iaa.AddToBrightness((-30, 30)),  # 亮度变化
    iaa.GaussianBlur(sigma=(0, 0.5)),  # 轻微模糊
    iaa.Sometimes(0.3, iaa.Rain(speed=(0.1, 0.3)))  # 随机添加雨天效果,增强无人机拍摄场景多样性
])

def augment_dataset(
    input_images_dir, 
    input_annotations_dir, 
    output_dir, 
    augmentations_per_image=3
):
    """对VisDrone数据集进行数据增强
  
    参数:
        input_images_dir: 输入图像目录
        input_annotations_dir: 输入标注目录
        output_dir: 输出目录
        augmentations_per_image: 每张原始图像生成的增强图像数量
    """
    # 创建输出目录
    output_images_dir = os.path.join(output_dir, "images")
    output_annotations_dir = os.path.join(output_dir, "annotations")
    os.makedirs(output_images_dir, exist_ok=True)
    os.makedirs(output_annotations_dir, exist_ok=True)
  
    # 获取所有图像文件
    image_files = glob.glob(os.path.join(input_images_dir, "*.jpg")) + \
                 glob.glob(os.path.join(input_images_dir, "*.png"))
  
    for img_path in tqdm(image_files, desc="增强数据集"):
        try:
            # 读取图像
            img = cv2.imread(img_path)
            if img is None:
                print(f"警告: 无法读取图像 {img_path}")
                continue
              
            # 获取对应的标注文件
            basename = os.path.splitext(os.path.basename(img_path))[0]
            ann_path = os.path.join(input_annotations_dir, f"{basename}.txt")
          
            if not os.path.exists(ann_path):
                print(f"警告: 找不到对应的标注文件 {ann_path}")
                continue
              
            # 读取标注
            bboxes = []
            bbox_categories = []
            with open(ann_path, 'r') as f:
                for line in f:
                    parts = line.strip().split(',')
                    if len(parts) >= 6:  # VisDrone格式
                        try:
                            x, y, w, h = map(int, parts[:4])
                            category_id = int(parts[5])
                          
                            # 排除忽略区域(category_id=0)或无效框
                            if category_id > 0 and w > 0 and h > 0:
                                bboxes.append(BoundingBox(x1=x, y1=y, x2=x+w, y2=y+h))
                                bbox_categories.append(category_id)
                        except (ValueError, IndexError):
                            continue
          
            # 初始化BoundingBoxesOnImage对象
            bbs = BoundingBoxesOnImage(bboxes, shape=img.shape)
          
            # 生成多个增强版本
            for aug_idx in range(augmentations_per_image):
                # 应用数据增强
                aug_img, aug_bbs = seq(image=img, bounding_boxes=bbs)
              
                # 确保所有边界框都在图像边界内
                aug_bbs = aug_bbs.remove_out_of_image().clip_out_of_image()
              
                # 保存增强后的图像
                aug_image_filename = f"{basename}_aug{aug_idx}.jpg"
                aug_image_path = os.path.join(output_images_dir, aug_image_filename)
                cv2.imwrite(aug_image_path, aug_img)
              
                # 保存增强后的标注
                aug_annotation_filename = f"{basename}_aug{aug_idx}.txt"
                aug_annotation_path = os.path.join(output_annotations_dir, aug_annotation_filename)
              
                with open(aug_annotation_path, 'w') as f:
                    valid_box_count = 0
                    for i, bbox in enumerate(aug_bbs.bounding_boxes):
                        if i >= len(bbox_categories):
                            break
                          
                        # 取整数值
                        x1, y1, x2, y2 = map(int, [bbox.x1, bbox.y1, bbox.x2, bbox.y2])
                        w, h = x2 - x1, y2 - y1
                      
                        # 排除无效框
                        if w <= 1 or h <= 1:
                            continue
                          
                        # VisDrone格式:
                        # <bbox_left>,<bbox_top>,<bbox_width>,<bbox_height>,<score>,<object_category>,<truncation>,<occlusion>
                        f.write(f"{x1},{y1},{w},{h},1,{bbox_categories[i]},0,0\n")
                        valid_box_count += 1
              
                if valid_box_count == 0:
                    # 如果没有有效框,删除这个增强样本
                    os.remove(aug_image_path)
                    os.remove(aug_annotation_path)
          
        except Exception as e:
            print(f"处理 {img_path} 时出错: {e}")
            continue
  
    print(f"数据增强完成! 增强后的数据保存在 {output_dir}")

# 示例调用
if __name__ == "__main__":
    import argparse
  
    parser = argparse.ArgumentParser(description="VisDrone数据集增强工具")
    parser.add_argument("--input_dir", type=str, required=True, help="输入数据集目录")
    parser.add_argument("--output_dir", type=str, required=True, help="输出增强数据集目录")
    parser.add_argument("--augmentations", type=int, default=3, help="每张图像的增强版本数量")
    args = parser.parse_args()
  
    # 查找images和annotations子目录
    base_dir = args.input_dir
    for root, dirs, _ in os.walk(base_dir):
        if "images" in dirs and "annotations" in dirs:
            input_images_dir = os.path.join(root, "images")
            input_annotations_dir = os.path.join(root, "annotations")
            print(f"找到数据集: {root}")
          
            augment_dataset(
                input_images_dir, 
                input_annotations_dir, 
                args.output_dir, 
                args.augmentations
            )
            break
    else:
        print(f"错误: 在 {base_dir} 下未找到 'images' 和 'annotations' 目录结构")

这个脚本可以帮助增强VisDrone数据集,通过添加旋转、翻转、亮度变化和天气效果等,提高模型的泛化能力,尤其是对无人机在各种天气条件下捕获的图像。

11. 在实际项目中的应用案例

11.1 智慧城市场景

  1. 使用VisDrone数据集训练道路交通监控系统
python
# 使用车辆与行人类别的子集生成专用数据集
def create_traffic_monitoring_subset():
    import shutil
    from tqdm import tqdm
  
    source_images_dir = "data/VisDrone2019-DET-train/images"
    source_annots_dir = "data/VisDrone2019-DET-train/annotations"
    target_dir = "data/traffic_monitoring_subset"
  
    # 创建目标目录
    target_images_dir = os.path.join(target_dir, "images")
    target_annots_dir = os.path.join(target_dir, "annotations")
    os.makedirs(target_images_dir, exist_ok=True)
    os.makedirs(target_annots_dir, exist_ok=True)
  
    # 目标类别:行人(1), 人群(2), 汽车(4), 面包车(5), 卡车(6), 公交车(9)
    target_categories = {1, 2, 4, 5, 6, 9}
  
    ann_files = os.listdir(source_annots_dir)
    for ann_file in tqdm(ann_files, desc="创建交通监控子集"):
        ann_path = os.path.join(source_annots_dir, ann_file)
      
        # 读取标注文件,检查是否包含目标类别
        relevant_objects = 0
        filtered_lines = []
      
        with open(ann_path, 'r') as f:
            for line in f:
                parts = line.strip().split(',')
                if len(parts) >= 6:
                    category_id = int(parts[5])
                    if category_id in target_categories:
                        relevant_objects += 1
                        filtered_lines.append(line)
      
        # 如果图像包含至少3个相关目标,将其添加到子集
        if relevant_objects >= 3:
            # 复制图像
            img_name = os.path.splitext(ann_file)[0] + ".jpg"
            img_src = os.path.join(source_images_dir, img_name)
            img_dst = os.path.join(target_images_dir, img_name)
          
            if os.path.exists(img_src):
                shutil.copy(img_src, img_dst)
              
                # 创建过滤后的标注文件
                ann_dst = os.path.join(target_annots_dir, ann_file)
                with open(ann_dst, 'w') as f:
                    f.writelines(filtered_lines)
                  
    print(f"交通监控子集已创建在 {target_dir}")

11.2 农业监测应用

  1. 使用VisDrone数据集训练无人机农田巡检系统
python
# 修改本教程中的数据集转换器,添加农田监测特定类别
def customize_for_agriculture(output_label_map_path):
    """创建农业监测的标签映射文件"""
    # 原始VisDrone类别
    visdrone_categories = {
        0: 'ignored-regions', 1: 'pedestrian', 2: 'people', 3: 'bicycle',
        4: 'car', 5: 'van', 6: 'truck', 7: 'tricycle', 8: 'awning-tricycle',
        9: 'bus', 10: 'motor', 11: 'others'
    }
  
    # 为农业监测重新映射类别
    agriculture_mapping = {
        # 保留部分原始类别
        4: 'farm-vehicle',       # 原car变为farm-vehicle
        5: 'trailer',            # 原van变为trailer
        6: 'harvester',          # 原truck变为harvester
        7: 'tractor',            # 原tricycle变为tractor
        1: 'farm-worker',        # 原pedestrian变为farm-worker
        10: 'irrigation-device', # 原motor变为irrigation-device
        11: 'crop-anomaly'       # 原others变为crop-anomaly
    }
  
    # 写入新的标签映射文件
    with open(output_label_map_path, 'w') as f:
        for id, name in agriculture_mapping.items():
            f.write(f"item {{\n")
            f.write(f"  id: {id}\n")
            f.write(f"  name: '{name}'\n")
            f.write(f"}}\n\n")
  
    print(f"农业监测标签映射已保存至 {output_label_map_path}")
    return agriculture_mapping

11.3 安全监控场景

  1. 使用VisDrone数据集训练无人机安全巡检系统
python
def detect_zone_intrusion(model_path, test_image_path, restricted_zone_coords):
    """使用训练好的模型检测限制区域内的入侵行为
  
    参数:
        model_path: 训练好的目标检测模型路径
        test_image_path: 测试图像路径
        restricted_zone_coords: 限制区域的多边形坐标,格式为 [(x1,y1), (x2,y2), ...]
    """
    import cv2
    import numpy as np
    import tensorflow as tf
    from shapely.geometry import Point, Polygon
  
    # 加载模型
    model = tf.saved_model.load(model_path)
    detect_fn = model.signatures['serving_default']
  
    # 读取图像
    image = cv2.imread(test_image_path)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    input_tensor = tf.convert_to_tensor([image_rgb], dtype=tf.uint8)
  
    # 执行检测
    detections = detect_fn(input_tensor)
  
    # 处理检测结果
    boxes = detections['detection_boxes'][0].numpy()
    scores = detections['detection_scores'][0].numpy()
    classes = detections['detection_classes'][0].numpy().astype(np.int32)
  
    # 创建限制区域多边形
    restricted_zone = Polygon(restricted_zone_coords)
  
    # 设置检测阈值
    threshold = 0.5
  
    # 检测结果处理和可视化
    h, w, _ = image.shape
    intrusion_detected = False
  
    # 绘制限制区域
    zone_points = np.array(restricted_zone_coords, np.int32)
    zone_points = zone_points.reshape((-1, 1, 2))
    cv2.polylines(image, [zone_points], True, (0, 0, 255), 2)
  
    for i in range(len(scores)):
        if scores[i] > threshold:
            # 获取实际像素坐标
            box = boxes[i]
            y1, x1, y2, x2 = int(box[0] * h), int(box[1] * w), int(box[2] * h), int(box[3] * w)
          
            # 计算边界框的中心点
            center_x = (x1 + x2) // 2
            center_y = (y1 + y2) // 2
            center_point = Point(center_x, center_y)
          
            # 检查中心点是否在限制区域内
            if restricted_zone.contains(center_point):
                # 区域内入侵对象用红色显示
                cv2.rectangle(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
                label = f"Class: {classes[i]}, Score: {scores[i]:.2f}, INTRUSION!"
                cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                intrusion_detected = True
            else:
                # 区域外对象用绿色显示
                cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
                label = f"Class: {classes[i]}, Score: {scores[i]:.2f}"
                cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
  
    # 在图像上显示警告状态
    if intrusion_detected:
        warning_text = "⚠️ INTRUSION DETECTED ⚠️"
        cv2.putText(image, warning_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
  
    # 保存结果
    result_path = test_image_path.replace('.jpg', '_intrusion_result.jpg')
    cv2.imwrite(result_path, image)
    print(f"结果已保存至 {result_path}")
  
    return intrusion_detected, result_path

11.4 工业应用示例

  1. 使用 VisDrone 数据集训练矿区安全监控系统
python
# 矿区无人机监控示例使用案例
def mining_site_monitoring_demo():
    # 示例代码:矿区监控应用
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import Rectangle
  
    def analyze_mining_site_image(image_path, model):
        """分析矿区航拍图像"""
        # 读取图像
        image = cv2.imread(image_path)
        image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
      
        # 模拟目标检测结果
        # 实际应用中,这里会调用加载好的模型进行检测
        detections = {
            'equipment': [(150, 200, 50, 30), (300, 250, 70, 40)],  # x, y, w, h
            'vehicle': [(450, 300, 60, 30), (200, 400, 80, 50)],
            'personnel': [(100, 150, 20, 40), (350, 180, 20, 40), (500, 350, 20, 40)]
        }
      
        # 创建可视化
        plt.figure(figsize=(12, 10))
        plt.imshow(image_rgb)
      
        # 绘制检测结果
        colors = {'equipment': 'blue', 'vehicle': 'green', 'personnel': 'red'}
        for obj_type, boxes in detections.items():
            for box in boxes:
                x, y, w, h = box
                rect = Rectangle((x, y), w, h, linewidth=2, edgecolor=colors[obj_type], facecolor='none')
                plt.gca().add_patch(rect)
                plt.text(x, y-5, obj_type, color=colors[obj_type], fontsize=10)
      
        # 检查危险情况:人员与设备距离过近
        safety_issues = []
        for person_box in detections['personnel']:
            px, py, pw, ph = person_box
            person_center = (px + pw/2, py + ph/2)
          
            # 检查与设备的距离
            for equip_box in detections['equipment']:
                ex, ey, ew, eh = equip_box
                equip_center = (ex + ew/2, ey + eh/2)
              
                # 计算距离
                distance = np.sqrt((person_center[0] - equip_center[0])**2 + 
                                  (person_center[1] - equip_center[1])**2)
              
                # 如果距离小于阈值,标记为不安全
                if distance < 100:  # 像素距离阈值
                    safety_issues.append((person_box, equip_box))
                    # 绘制连接线表示危险关系
                    plt.plot([person_center[0], equip_center[0]], 
                             [person_center[1], equip_center[1]], 
                             'y--', linewidth=2)
      
        plt.title('矿区安全监控分析')
        plt.axis('off')
      
        # 添加图例
        for obj_type, color in colors.items():
            plt.plot([], [], color=color, label=obj_type)
        if safety_issues:
            plt.plot([], [], 'y--', label='安全隐患')
        plt.legend()
      
        # 保存结果
        result_path = image_path.replace('.jpg', '_mining_analysis.jpg')
        plt.savefig(result_path, dpi=300, bbox_inches='tight')
      
        return {
            'total_equipment': len(detections['equipment']),
            'total_vehicles': len(detections['vehicle']),
            'total_personnel': len(detections['personnel']),
            'safety_issues': len(safety_issues),
            'result_image': result_path
        }
  
    # 模拟调用分析函数
    print ("模拟矿区无人机监控系统...")
    Result = analyze_mining_site_image ('example_mining_site.jpg', None)
    print (f"分析结果: 检测到 {result['total_equipment']} 台设备, {result['total_vehicles']} 辆车辆, "
          f"{result['total_personnel']} 名人员")
    print (f"发现 {result['safety_issues']} 处安全隐患")
    print (f"结果图像已保存到: {result['result_image']}")

12. 课程实验与报告指南

为完成林云老师布置的无人机相关作业,以下是实验步骤和报告编写指南:

12.1 实验步骤详细规划

步骤 1: 准备环境与数据集

bash
# 配置开发环境
# 创建虚拟环境
conda create -n visdrone python=3.8 -y
conda activate visdrone

# 安装依赖
pip install opencv-python matplotlib numpy pandas seaborn scikit-learn \
    Jupyter notebook tqdm tensorflow pillow imgaug shapely

# 下载 VisDrone 数据集
Mkdir -p ~/VisDrone_datasets
Cd ~/VisDrone_datasets

# 从官方网站手动下载并上传到服务器
# https://github.com/VisDrone/VisDrone-Dataset

# 解压数据集
tar -xf VisDrone 2019-DET-train.tar

步骤 2: 实验流程记录

  1. 应用本教程的可视化工具:

    bash
    # 克隆工具库
    git clone https://github.com/your_username/VisDrone-dataset-python-toolkit.git
    Cd VisDrone-dataset-python-toolkit
    
    # 运行可视化脚本
    python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone 2019-DET-train --max_vis_images 10
  2. 格式转换演示:

    bash
    # COCO 格式转换
    python visdrone 2 coco.py --data_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir coco_output
    
    # YOLO 格式转换
    python visdrone 2 yolo.py --data_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir yolo_output
  3. 数据集统计分析:

    bash
    # 运行 Jupyter Notebook 进行交互式分析
    Jupyter notebook visdrone_analysis. Ipynb
  4. 数据增强演示:

    bash
    # 运行数据增强脚本
    python data_augmentation.py --input_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir augmented_data --augmentations 3

12.2 实验报告编写指南

一、引言

  • 无人机图像识别技术的背景和重要性
  • VisDrone 数据集介绍和特点
  • 实验目标和内容概述

二、理论基础

  • 目标检测算法基础
  • 无人机视角下的目标检测挑战
  • VisDrone 数据集标注格式解析

三、实验环境与配置

  • 硬件环境
  • 软件环境(系统版本、Python 版本、相关库版本)
  • 数据集获取与预处理

四、数据可视化与分析

  • 可视化工具实现原理
  • 数据集样本展示
  • 标注信息统计与分析
    • 类别分布
    • 目标尺寸分布
    • 目标位置分布热力图

五、数据格式转换实现

  • COCO 格式转换原理与实现
  • YOLO 格式转换原理与实现
  • 转换结果验证

六、数据增强技术应用

  • 增强策略选择原理
  • Imgaug 库应用方法
  • 增强效果对比与展示

七、应用案例设计

  • 选择一个具体应用场景(安全监控/交通监控/农业监测等)
  • 应用流程设计
  • 关键代码实现与解析

八、实验结果与分析

  • 各工具的执行结果
  • 数据集特点总结
  • 应用案例效果评估

九、结论与展望

  • 实验总结
  • VisDrone 数据集的优缺点分析
  • 未来改进方向

十、参考文献

  • 格式规范的文献引用列表

12.3 录屏与演示建议

按照林云老师的要求进行完整的实验过程录屏,建议按以下几个部分进行:

  1. 环境配置录屏

    • 操作系统环境展示
    • Python 环境配置
    • 依赖包安装
  2. 数据集预处理录屏

    • 数据集下载/解压过程
    • 数据集结构介绍
    • 示例图像与标注展示
  3. 工具使用录屏

    • 可视化工具运行过程
    • 格式转换工具运行过程
    • Jupyter Notebook 中的数据分析过程
  4. 应用场景演示录屏

    • 选择的应用案例执行过程
    • 结果展示与解释

在录屏过程中,建议:

  • 使用麦克风实时解说操作步骤和原理
  • 放慢关键步骤的操作速度,确保观众能够跟上
  • 对遇到的错误及解决方案进行详细说明
  • 在录屏结束时对整个实验过程进行总结

13. 总结与进一步学习资源

13.1 教程总结

本教程全面介绍了VisDrone数据集的处理与应用方法,包括:

  1. 数据集基础:详细解析了VisDrone目标检测数据集的结构、标注格式和类别定义。
  2. 可视化工具:提供了多种可视化方法,帮助直观理解数据集特点。
  3. 数据分析:通过统计分析揭示了数据集的类别分布、目标尺寸特征和密度特性。
  4. 格式转换:实现了从VisDrone原始格式到COCO、YOLO等主流格式的转换工具。
  5. 数据增强:提供了针对无人机视角特点的数据增强方法。
  6. 应用案例:展示了VisDrone数据集在智慧城市、安全监控和农业等领域的实际应用。

通过掌握这些工具和方法,你可以高效地利用VisDrone数据集来训练和优化目标检测模型,解决无人机视角下的各种实际问题。

13.2 进一步学习资源

为了深入研究无人机视觉领域,以下是推荐的学习资源:

学术论文

  1. Zhu, P., Wen, L., Du, D., Bian, X., Ling, H., et al. (2020). Vision Meets Drones: Past, Present and Future. arXiv preprint arXiv:2001.06303.

  2. Wen, L., Zhu, P., Du, D., Bian, X., et al. (2019). VisDrone-DET2019: The Vision Meets Drone Object Detection in Image Challenge Results. ICCV Workshop.

  3. Bozcan, I., & Kalkan, S. (2021). ADDR: Anomaly Detection in Drone Recordings Using Spatio-Temporal Autoencoders. IEEE Robotics and Automation Letters.

在线课程和教程

  1. 无人机遥感与计算机视觉 - 清华大学/Stanford大学联合课程

  2. Coursera: Aerial Robotics - 宾夕法尼亚大学 这门课程涵盖了无人机的基本原理,包括感知系统。

  3. Udacity: Flying Car Nanodegree 包含无人机计算机视觉和感知系统的专业课程。

开源项目与代码库

  1. MMDetection - 支持VisDrone数据集的目标检测工具箱 GitHub: open-mmlab/mmdetection

  2. DroneVision - 无人机视觉处理框架 GitHub: VisDrone (VisDrone)

  3. AerialDetection - 专注于航空图像目标检测的工具库 GitHub: dingjiansw101/AerialDetection

竞赛与挑战

  1. VisDrone Challenge - 年度无人机视觉算法竞赛 网址:VisDrone Challenge

  2. NTIRE Workshop - 经常举办无人机图像处理相关竞赛 网址:NTIRE Workshop

  3. Kaggle - 定期举办无人机图像分析相关竞赛

数据集

  1. UAVid - 无人机城市场景语义分割数据集 网址:UAVid

  2. UAVDT - 无人机视角下的目标检测与跟踪数据集 网址:UAVDT

  3. Aerial Image Dataset - 航空图像分析集合 网址:Aerial Image Dataset

13.3 实用工具推荐

  1. LabelImg - 适用于目标检测标注的图形界面工具 GitHub: tzutalin/labelImg

  2. CVAT - 强大的计算机视觉标注工具,支持视频和图像 GitHub: opencv/cvat

  3. Roboflow - 提供数据集管理、增强和格式转换的在线平台 网址:Roboflow

  4. ImgAug - 本教程中使用的图像增强库 GitHub: aleju/imgaug

  5. Albumentations - 高性能的图像增强库 GitHub: albumentations-team/albumentations

13.4 致谢与贡献指南

感谢所有为VisDrone数据集做出贡献的研究者,特别是来自天津大学、布里斯托大学等机构的团队。本教程旨在帮助更多研究者和开发者有效利用这一宝贵资源。

如果您想为本教程做出贡献,请通过以下方式参与:

  • 提交Bug报告或功能请求
  • 贡献新的数据处理或可视化代码
  • 分享您使用VisDrone数据集的实际应用案例
  • 改进文档或添加更多教程内容

13.5 许可证信息

本教程中的代码采用MIT许可证发布,您可以自由使用、修改和分发这些代码,但需保留原始版权声明。请注意,VisDrone数据集本身有其特定的使用条款,使用前请查阅官方网站。

贡献者

The avatar of contributor named as RainbowRain9 RainbowRain9
The avatar of contributor named as Cai Hongyu Cai Hongyu

文件历史

撰写